#include<stdio.h>
#include<iostream>
#include<string.h>
#include<cstdlib>
#include<unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

#define COMMAND_SIZE 1024
#define FORMAT "[%s@%s %s]# "

//shell定义的全局数据
#define MAXARGC 128
char * g_argv[MAXARGC];
int g_argc=0;
char cwd[1024];
char cwdenv[1024];

//环境变量表
#define MAX_ENVS 100

char *g_env[MAX_ENVS];
int g_envs=0;


//退出码
int lastcode=0;
//=================在PATH中获取数据======
const char*GetUserName()//获取用户名
{
  const char*name =getenv("USER");
  return name==NULL?"None":name;//用户名获取不到的话返回Node否则返回用户名
}

const char* GetHostName()//获取HOST名
{
  const char *HostName=getenv("HOSTNAME");
  return HostName==NULL?"None":HostName;
}

const char *GetPwd()//获取当前的路径
{
  const char *pwd=getcwd(cwd,sizeof(cwd));
  if(pwd!=NULL)
  {
    snprintf(cwdenv,sizeof(cwdenv),"PWD=%s",cwd);
    putenv(cwdenv);
  }
  return pwd==NULL?"None":pwd;
}

//获取家目录
const char*GetHome()
{
  const char *home = getenv("HOME");
  return home ==NULL?"":home;
}
//当前用户的主目录  OLDPWD 
const char*GetOLDPWD()
{
  const char *OLD=getenv("OLDPWD");
  return OLD==NULL?"~":OLD;
}
//==========================================

//=======路径切割,只保留末尾的路径
std::string DirName(const char*pwd)
{
#define XG "/"
  std::string dir=pwd;
  if(dir==XG)return  XG;
  auto pos =dir.rfind(XG);//从后往前找
  if(pos==std::string::npos)return "BUG?";//如果没有找到斜杠那么表示出BUG了
  return dir.substr(pos+1);//返回斜杠之后的字符
}


//command
bool CD()
{

      std::string where; 
      if(g_argc==1)//如果g_argc==1表明没有传入有效参数 比如:cd  后面没有跟参数
      {
        std::string home = GetHome();
          if(home.empty())return true;//如果传进来的home地址为空那么表明获取是失败则直接返回
          chdir(home.c_str());
      }else
      {
         where=g_argv[1];//把想去的地址拿到
        //有时候我们使用cd的时候会cd - 或者cd ~ 等等所以我们需要做更多的判断
        if(where=="-")
        {

        }else if(where=="~"||where=="")
        {
            
        }else
        {
          chdir(where.c_str());
        }
      }
  return true;
}


void MakeCommandLine(char cmdline[],int size)
{
  snprintf(cmdline,size,FORMAT,GetUserName(),GetHostName(),DirName(GetPwd()).c_str());
  
}

void PrintCommandPrompt()
{
  char prompt[COMMAND_SIZE];
  MakeCommandLine(prompt,sizeof(prompt));
  printf("%s",prompt);
  fflush(stdout);
}

//===获取命令符
bool GetCmdLine(char*out,int size)
{
  
  const char* c=fgets(out,size,stdin);
  if(c==NULL)return 1;
  out[strlen(out)-1]=0;//去除多余的\n
  if(strlen(out)-1==0)return false;
  return true;
}

//===分析shell命令
bool CommandParse(char *commandline)
{
#define SEP " "
  g_argc=0;
  g_argv[g_argc++]=strtok(commandline,SEP);
  while((bool)(g_argv[g_argc++]=strtok(nullptr,SEP)));
 
  g_argc--;
  return g_argc>0?true:false;//如果传入的命令不是空的就返回真,否则反回假 
}


void PrintArgv()//打印测试
{
  for(int i=0;g_argv[i];i++)
  {
    printf("argv[%d]->%s\n",i,g_argv[i]);
  }
  printf("argv:%d\n",g_argc);
}
//检测内建命令
bool CheackAndExecBuiltin()
{
  std::string cmd=g_argv[0];
    if(cmd=="cd")
    {
      CD();
      return true;
    }else if(cmd=="echo")
    {
      if(g_argc==2)//如果echo的命令参数为2
      {

           //判断一下echo后面接的是什么命令然后再做执行
          //echo "hello world"
          //echo $?
          //echo $PATH 等等
          std::string opt = g_argv[1];
          if(opt=="$?")
          {
              std::cout<<lastcode<<std::endl;
              lastcode=0;
          }else if(opt=="$")
          {
            std::string env_name=opt.substr(1);
            const char *env_value=getenv(env_name.c_str());
            if(env_value)
            {
                std::cout<<env_name<<std::endl;
            }

          }

      }

                return true;
    } 
    return false;
}

//===执行命令
int Execute()
{
  
    pid_t id=fork();
    if(id==0)
    {
      //子进程
      execvp(g_argv[0],g_argv);
      exit(1); 
    }
    
    int status=0;

    pid_t rid=waitpid(id,&status,0);
    if(rid>0)//等到进程退出
    {
        lastcode=WEXITSTATUS(status);
    }
    return 0;
}



int main()
{
  while(1)//因为shell是不会停止的所以我们需要一直循环
  {
      
  //1.输出命令提示符 
    PrintCommandPrompt();

    //2.获取用户输入的命令
    char commandline[COMMAND_SIZE];
    if(!GetCmdLine(commandline,sizeof(commandline)))
    {
      continue;
    }
    //3.命令行分析"ls -a -l"->"ls" "-a" "-l"
   if(! CommandParse(commandline))
   {
      continue;
   };
    //PrintArgv();
    
    //4.检测内建命令
    if(CheackAndExecBuiltin())
    {
      continue;
    }
    //5.执行
    Execute();//执行命令
  } 
  return 0;
}
